#include "global.h"
#include "ext2.h"

typedef struct header
{
	long sep;
	long gap;
	long tsize;
	long dsize;
	long bsize;
	long zero;
	long tot;
	long ssize;
} Header;

// Gets the specified disk block and stores it in buf
void getblk(unsigned int blk, char* buf)
{
	blk *= 2;
	diskr(blk / 36, (blk / 18) % 2, blk % 18, buf);
}

// Gets the inode with the specified inodeNum
Ext2Inode* getInode(char* buf, unsigned int inodeNum)
{
	getblk((inodeNum / 8) + 5, buf);
	return ((Ext2Inode*)(buf + (128 * ((inodeNum - 1) % 8))));
}

// Finds an Ext2DirEnt with the specified name in the given block
Ext2DirEnt* findDirEnt(char* blk, char* name)
{
	int i = 0, j = 0;
	char tmpName[256];
	while (i < 1024)
	{
		for (j = 0; j < ((Ext2DirEnt*)(blk))->name_len; ++j)
		{
			tmpName[j] = ((Ext2DirEnt*)(blk))->name[j];
		}
		tmpName[j] = '\0';
		if (strcmp(name, tmpName) == 0)
			return blk;
		i += ((Ext2DirEnt*)(blk))->rec_len;
		blk += ((Ext2DirEnt*)(blk))->rec_len;
	}
	return 0;
}

void putWord(s16 word, s16 segment, s16 offset)
{
	s8 byte1 = *((s8*)(&word));
	s8 byte2 = *(((s8*)(&word)) + 1);
	put_byte(byte1, segment, offset);
	put_byte(byte2, segment, offset + 1);
}

s16 getWord(s16 segment, s16 offset)
{
	s16 word;
	*((s8*)(&word)) = get_byte(segment, offset);
	*(((s8*)(&word)) + 1) = get_byte(segment, offset + 1);
	return word;
}

int load(char* path, unsigned int segment)
{
	int i = 0, j = 0, numPathTokens = 1;
	int size = 0, offset = 0, block = 1;
	char buf[1024], *ptr = buf;
	char* curToken = path + 1;
	Ext2DirEnt* curDirEnt = 0;
	Ext2Inode curInode;
	Header head;
	
	// First check that the pathname is absolute
	if (path[0] != '/')
	{
		printf("\nPathname must be absolute!\n");
		return 0;
	}
	
	// Remove the first '/' char
	++path;
	
	// Break up pathname into tokens by replacing each '/' with a '\0'
	//printf("\nBreaking path into tokens...\n");
	for (i = 0; path[i] != '\0'; ++i)
	{
		if (path[i] == '/')
		{
			path[i] = '\0';
			//printf("%s -> ", curToken);
			curToken += strlen(curToken) + 1;
			++numPathTokens;
		}
	}
	//printf("%s\n", curToken);
	
	// Read in the root inode
	getblk(5, buf);
		
	// Read the root inodes first data block
	i = ((Ext2Inode*)(buf + 128))->i_block[0];
	getblk(i, buf);
	
	// Continue to find the file at pathname
	curToken = path;
	for (i = 0; i < numPathTokens; ++i)
	{
		//printf("\nLooking for %s...", curToken);
		curDirEnt = findDirEnt(buf, curToken);
		if (curDirEnt == 0)
		{
			printf("Unable to locate file!\n");
			return 0;
		}
		//printf("Getting inode %d\n", curDirEnt->inode);
		curInode = *(getInode(buf, curDirEnt->inode));
		
		if (i != numPathTokens - 1)
		{
			j = curInode.i_block[0];
			getblk(j, buf);
			
			curToken += strlen(curToken) + 1;
		}
	}
	
	// Load the first data block to get the header information
	i = curInode.i_block[0];
	getblk(i, buf);
	
	// Get the header information and size of the program
	head = *((Header*)(buf));
	//printf("\ntsize = %l dsize = %l bsize = %l\n", head.tsize,
	//	head.dsize, head.bsize);		
	size = head.tsize + head.dsize + head.bsize;
	
	// Move the program into memory, cutting off the head
	ptr = buf + 32;
	for (i = 32; offset < size; ++i)
	{
		if ((i % 1024) == 0)
		{
			j = curInode.i_block[block++];
			getblk(j, buf);
			ptr = buf;
		}
		put_byte(*(ptr++), segment, offset++);
	}
	
	return 1;
}
